﻿using System;
using System.Collections.Generic;
using System.Windows.Forms;

using Microsoft.SharePoint.Deployment;
using Microsoft.SharePoint;

using WizardBase;
using COB.SharePoint.Utilities.DeploymentWizard.Core;
//using COB.SharePoint.Utilities.DeploymentWizard.Core.Helpers;

namespace COB.SharePoint.Utilities.DeploymentWizard.UI
{
    class SiteTreeNavigator
    {
        // Internal data
        private static readonly string f_csNODES_NOT_RETRIEVED_TEXT = "Please rebind to site to see these nodes - return to previous step by clicking 'Back', then click 'Next'.";

        public static readonly string f_csSITE_ICON_IDENTIFIER = "SiteIcon";
        public static readonly string f_csLIST_ICON_IDENTIFIER = "ListIcon";
        public static readonly string f_csFOLDER_ICON_IDENTIFIER = "FolderIcon";
        public static readonly string f_csFILE_ICON_IDENTIFIER = "FileIcon";

        private TreeView f_treeView = null;

        private int f_iDisposedWebsCount = 0;
        private int f_iDisposedWebsFromMenuCount = 0;

        private DisplayListsSetting m_displayLists = DisplayListsSetting.DisplayLists;
        private DisplayItemsSetting m_displayItems = DisplayItemsSetting.DisplayItems;

        // Events

        public delegate void OnNewNodeHandler(TreeNode node, SPObjectData objectData);
        public event OnNewNodeHandler OnDisplayNode;

        public delegate void OnListHandler(SPList list, ref bool display);
        public event OnListHandler OnDisplayList;

        // Enums & Types

        public enum DisplayListsSetting { DisplayLists, NoDisplayLists };
        public enum DisplayItemsSetting { DisplayItems, NoDisplayItems };

        // Properties
        public DisplayListsSetting DisplayLists { get { return m_displayLists; } set { m_displayLists = value; } }
        public DisplayItemsSetting DisplayItems { get { return m_displayItems; } set { m_displayItems = value; } }

        // Constructor
        public SiteTreeNavigator(TreeView treeView)
        {
            f_treeView = treeView;
        }

        // Methods (public methods begin with capital letter)
        public void AddNodesForWeb(SPWeb web)
        {
            addNodesForWeb(web, null);
        }

        private void addNodesForWeb(SPWeb web, TreeNode parentNode)
        {
            //TreeNode rootWebNode = null;
            TreeNode webNode = null;
            TreeNode listNode = null;

            if (parentNode == null)
            {
                SPSite parentSite = web.Site;

                // add node for root web..
                parentNode = f_treeView.Nodes.Add(web.Url);
                parentNode.Name = parentSite.ID.ToString();
                parentNode.Tag = getObjectData(parentSite);
                completeNewNode(parentNode);
            }

            // add immediate subwebs..
            foreach (SPWeb childWeb in web.Webs)
            {
                webNode = new TreeNode(childWeb.Url);
                webNode.Name = childWeb.ID.ToString();
                webNode.Tag = getObjectData(childWeb);
                webNode.ImageKey = f_csSITE_ICON_IDENTIFIER;
                webNode.SelectedImageKey = f_csSITE_ICON_IDENTIFIER;
                completeNewNode(webNode);
                if (m_displayLists == DisplayListsSetting.DisplayLists || childWeb.Webs.Count > 0)
                {
                    // add a dummy child so node can be expanded
                    webNode.Nodes.Add(f_csNODES_NOT_RETRIEVED_TEXT);
                }

                parentNode.Nodes.Add(webNode);

                childWeb.Dispose();
            }

            // add immediate lists..
            if (m_displayLists == DisplayListsSetting.DisplayLists)
            {
                foreach (SPList list in web.Lists)
                {
                    if (listIsContent(list))
                    {
                        listNode = new TreeNode(string.Format("{0} ({1})",
                            getListFriendlyName(list, false), list.ItemCount));
                        listNode.Name = list.ID.ToString();
                        listNode.Tag = getObjectData(list);
                        listNode.ImageKey = f_csLIST_ICON_IDENTIFIER;
                        listNode.SelectedImageKey = f_csLIST_ICON_IDENTIFIER;

                        completeNewNode(listNode);
                        if (m_displayItems == DisplayItemsSetting.DisplayItems && list.ItemCount > 0)
                        {
                            // add a dummy child so node can be expanded
                            listNode.Nodes.Add(f_csNODES_NOT_RETRIEVED_TEXT);
                        }

                        parentNode.Nodes.Add(listNode);
                    }
                }
            }
        }

        private void addNodesForList(SPList list, TreeNode parentNode)
        {
            addNodesForFolder(list.RootFolder, parentNode);
        }

        private void addNodesForFolder(SPFolder folder, TreeNode parentNode)
        {
            TreeNode currentNode = null;

            SPQuery query = new SPQuery();
            query.Folder = folder;
            using (SPWeb parentWeb = folder.ParentWeb)
            {
                SPListItemCollection folderItems = parentWeb.Lists[folder.ParentListId].GetItems(query);
                foreach (SPListItem item in folderItems)
                {
                    currentNode = new TreeNode(item.Name);
                    currentNode.Name = item.UniqueId.ToString();
                    currentNode.Tag = getObjectData(item);

                    if (item.Folder != null)
                    {
                        currentNode.ImageKey = f_csFOLDER_ICON_IDENTIFIER;
                        currentNode.SelectedImageKey = f_csFOLDER_ICON_IDENTIFIER;
                    }
                    else
                    {
                        currentNode.ImageKey = f_csFILE_ICON_IDENTIFIER;
                        currentNode.SelectedImageKey = f_csFILE_ICON_IDENTIFIER;
                    }

                    completeNewNode(currentNode);

                    parentNode.Nodes.Add(currentNode);

                    // also recurse child folders..
                    if (item.Folder != null)
                    {
                        addNodesForFolder(item.Folder, currentNode);
                    }
                }
            }
        }

        public void PopulateObjectChildNodes(TreeNode parentNode, SPObjectData objectData)
        {
            // only add nodes if reference to object's SPWeb is valid and has not been disposed. If it is 
            // not valid, this indicates node has already been expanded and child nodes added..
            if (objectData.Web != null)
            {
                parentNode.Nodes.Clear();
                SPWeb currentWeb = objectData.Web;

                switch (objectData.ObjectType)
                {
                    case SPDeploymentObjectType.Web:
                        // get SPWeb object then pass to addNodesForWeb..
                        addNodesForWeb(currentWeb, parentNode);
                        currentWeb.Dispose();
                        // set objectData.Web to null for next pass as well as do disposal..
                        objectData.Web = null;
                        disposeWebs(parentNode, false);
                        break;
                    case SPDeploymentObjectType.List:
                        if (m_displayItems == DisplayItemsSetting.DisplayItems)
                        {
                            SPList currentList = currentWeb.Lists[objectData.ID];
                            addNodesForList(currentList, parentNode);
                            currentWeb.Dispose();
                        }
                        // set objectData.Web to null for next pass as well as do disposal..
                        objectData.Web = null;
                        disposeWebs(parentNode, false);
                        break;
                    case SPDeploymentObjectType.Folder:

                        break;
                    default:
                        break;
                }

                parentNode.Expand();
            }
        }

        // called after each new tree node created
        private void completeNewNode(TreeNode node)
        {
            // call through to addContextMenuToTreeViewNode(parentNode);
            if (OnDisplayNode != null)
            {
                OnDisplayNode(node, GetObjectDataFromNode(node));
            }
        }

        private SPObjectData GetObjectDataFromNode(TreeNode node)
        {
            SPObjectData objectData = (SPObjectData)node.Tag;
            return objectData;
        }
        // Unused -- Perry, 2009-12-23
        /*
        private void addNodesFromWeb(TreeView trvControl, TreeNode parentNode, SPWeb currentWeb)
        {
//            if (f_traceSwitch.TraceVerbose)
//            {
//                f_traceHelper.TraceVerbose("addNodesFromWeb: Entered addNodesFromWeb().");
//            }

            // add nodes from current web..
            TreeNode webNode = new TreeNode(currentWeb.Url);

            // slightly different behaviour if the site is actually the RootWeb for the site..
            if (parentNode == null)
            {
                webNode.Tag = getObjectData(currentWeb.Site);
                webNode.Expand();
            }
            else
            {
                webNode.Tag = getObjectData(currentWeb);
            }

            completeNewNode(webNode);

            if (parentNode == null)
            {
                trvControl.Nodes.Add(webNode);
            }
            else
            {
                parentNode.Nodes.Add(webNode);
            }

            // recurse child webs..
            foreach (SPWeb childWeb in currentWeb.Webs)
            {
                addNodesFromWeb(trvControl, webNode, childWeb);
                childWeb.Dispose();
            }

            SPListCollection colLists = currentWeb.Lists;
            foreach (SPList list in colLists)
            {
                if (listIsContent(list))
                {
                    addListNode(trvControl, webNode, list);
                }
            }

//            if (f_traceSwitch.TraceVerbose)
//            {
//                f_traceHelper.TraceVerbose("addNodesFromWeb: Exited addNodesFromWeb().");
//            }
        }
        */

        private void addListNode(TreeView trvContent, TreeNode parentNode, SPList list)
        {
//            if (f_traceSwitch.TraceVerbose)
//            {
//                f_traceHelper.TraceVerbose(string.Format("addListNode: Entered addListNode() with list '{0}'.",
//                    list.Title));
//            }

            TreeNode listRootNode = null;
            TreeNode currentNode = null;

            listRootNode = new TreeNode(string.Format("{0} ({1})",
                list.Title, list.ItemCount));
            listRootNode.Tag = getObjectData(list);

            completeNewNode(listRootNode);

            // now deal wih leaf items..
            foreach (SPListItem item in list.Items)
            {
                currentNode = new TreeNode(item.Name);
                currentNode.Tag = getObjectData(item);
                completeNewNode(currentNode);

                listRootNode.Nodes.Add(currentNode);
            }

            parentNode.Nodes.Add(listRootNode);

//            if (f_traceSwitch.TraceVerbose)
//            {
//                f_traceHelper.TraceVerbose("addListNode: Exited addListNode().");
//            }
        }
        private static SPObjectData getObjectData(SPSite site)
        {
            return new SPObjectData(site.ID, site.PortalName, site.Url, SPDeploymentObjectType.Site, SPIncludeDescendants.All);
        }

        private static SPObjectData getObjectData(SPWeb web)
        {
            return new SPObjectData(web.ID, web.Title, web.Url, SPDeploymentObjectType.Web,
                SPIncludeDescendants.All, web);
        }

        private static SPObjectData getObjectData(SPList list)
        {
            return new SPObjectData(list.ID, list.Title, getListFriendlyName(list, true), SPDeploymentObjectType.List,
                SPIncludeDescendants.All, list.ParentWeb);
        }

        private static SPObjectData getObjectData(SPListItem listItem)
        {
            SPObjectData listItemData = null;
            SPDeploymentObjectType objectType;
            string sListItemText = null;

            // also add file ID here for later processing for ListItems which have files..
            if (listItem.File != null)
            {
                using (SPWeb listParentWeb = listItem.Web)
                {
                    sListItemText = string.Format("{0}/{1}", listParentWeb.Url, listItem.Url);
                    objectType = SPDeploymentObjectType.File;
                }
            }
            else
            {
                sListItemText = string.Format("{0}/{1}", getListFriendlyName(listItem.ParentList, true), listItem.Name);

                if (listItem.Folder != null)
                {
                    objectType = SPDeploymentObjectType.Folder;
                }
                else
                {
                    objectType = SPDeploymentObjectType.ListItem;
                }
            }

            // Perry, 2011-06-06, fix to have correctly fully-qualified item url (not web-relative)
            // per http://spdeploymentwizard.codeplex.com/workitem/10150
            string itemurl = GetListItemUrl(listItem);
            listItemData = new SPObjectData(listItem.UniqueId, sListItemText, itemurl,
                    objectType, SPIncludeDescendants.All);
            return listItemData;
        }
        private static string GetListItemUrl(SPListItem listItem)
        { // added by Perry, 2011-06-06
            return CombineUrl(listItem.ParentList.ParentWebUrl, listItem.Url);
        }
        public static string CombineUrl(string url1, string url2)
        { // added by Perry, 2011-06-06
            return url1.TrimEnd('/') + "/" + url2.TrimStart('/');
        }

        private static string getListFriendlyName(SPList list, bool bFullName)
        {
            string sDefaultViewUrl = list.DefaultViewUrl;
            string sListUrl = list.Title;

            if (bFullName)
            {
                // build absolute URL..
                using (SPWeb parentWeb = list.ParentWeb)
                {
                    sListUrl = string.Format("{0}/{1}",
                        parentWeb.Url, sListUrl);
                }
            }

            return sListUrl;
        }

        private bool listIsContent(SPList list)
        {
            bool display = true;
            if (OnDisplayList != null)
            {
                OnDisplayList(list, ref display);
            }
            return display;
        }

        // Create and return context menu subitem for export tree nodes
        public ToolStripMenuItem CreateSubMenuItem(string text, SPObjectData objectData, SPIncludeDescendants includeDescendents)
        {
            // use same object type as parent
            return CreateSubMenuItem(text, objectData, includeDescendents, objectData.ObjectType);
        }
        // Create and return context menu subitem for export tree nodes
        public ToolStripMenuItem CreateSubMenuItem(string text, SPObjectData objectData, SPIncludeDescendants includeDescendents, SPDeploymentObjectType objtype)
        {
            ToolStripMenuItem menuItem = new ToolStripMenuItem();
            menuItem.Text = text;

            // overwrite value of 'include descendents' property as appropriate to this menu item..
            SPObjectData newObject = (SPObjectData)objectData.Clone();
            // In case overriding object type of parent (e.g., site context menu item for "Export Root Web only")
            newObject.ObjectType = objtype;
            newObject.IncludeDescendents = includeDescendents;
            menuItem.Tag = newObject;

            return menuItem;
        }
        public SPObjectData GetObjectFromContextMenuItem(ToolStripItem menuItem)
        {
            SPObjectData objectData = (SPObjectData)menuItem.Tag;
            return objectData;
        }

        public void DisposeAllWebs()
        {
            f_iDisposedWebsFromMenuCount = 0;
            f_iDisposedWebsCount = 0;
            disposeWebs(f_treeView.Nodes[0], true);
        }
        private void disposeWebs(TreeNode node, bool bRecurse)
        {
            SPObjectData objectData = (SPObjectData)node.Tag;
            disposeAssociatedWeb(objectData, false);

            // TODO: Figure out if this code needs to be pushed back into frmContentDeployer
            if (node.ContextMenuStrip != null)
            {
                foreach (ToolStripMenuItem menuItem in node.ContextMenuStrip.Items)
                {
                    objectData = (SPObjectData)menuItem.Tag;
                    disposeAssociatedWeb(objectData, true);

                    foreach (ToolStripMenuItem childMenuItem in menuItem.DropDown.Items)
                    {
                        objectData = (SPObjectData)childMenuItem.Tag;
                        disposeAssociatedWeb(objectData, true);
                    }
                }
            }

            if (bRecurse)
            {
                foreach (TreeNode childNode in node.Nodes)
                {
                    disposeWebs(childNode, bRecurse);
                }
            }
        }

        private void disposeAssociatedWeb(SPObjectData objectData, bool bFromContextMenu)
        {
            if (objectData != null)
            {
                if (objectData.Web != null)
                {
                    objectData.Web.Dispose();
                    objectData.Web = null;
                    if (bFromContextMenu)
                    {
                        f_iDisposedWebsFromMenuCount++;
                    }
                    else
                    {
                        f_iDisposedWebsCount++;
                    }
                }
            }
        }

    }
}
